FreeRTOS 任务与调度问答合集
由 15 篇星球问答重组而成,覆盖任务创建、任务栈、调度机制与优先级设计四大主题。原帖全部保留,见文末来源清单。
一、任务创建
xTaskCreate 和 osThreadNew 有本质区别吗?
没有。osThreadNew 是 CMSIS-RTOS2 标准对 xTaskCreate 的封装,只是参数形式不同,本质是同一个创建动作。
— xTaskCreate(LcdPrintTask, :task1:, 128,(Jack)
动态/静态创建任务,内存分别从哪来?
动态创建时,任务(TCB+栈)的空间申请自 FreeRTOS 自管理的堆,即 heap_4.c 中那个巨大的 ucHeap 数组。注意:原帖把"静态创建"表述为从单片机堆 malloc 分配,这与 FreeRTOS 官方语义相反(静态创建应由调用方传入现成缓冲区、不经 malloc),参考时需留意。
— 动态静态创建任务: 你动态创(Jack)
任务函数没有死循环会发生什么?
任务函数若执行到返回,会落到 pvTaskExitError。FreeRTOS 任务必须是死循环,或主动调用删除接口结束自己,不能"跑完就退"。
— 如果freertos中创建的任务没有死循环,会发生什么呢:(Jack)
为什么 CM3 初始化任务栈帧要把函数入口地址最低位清 0,CM0 却不用?
CM0 异常进出的 LSB 恒为 0;CM3 进入异常时硬件会把 PC 最低位(Thumb 位)清 0,退出异常时也要看到 0。FreeRTOS 初始化栈帧是在模拟一次"硬件异常入场",让首次调度像从 ISR 返回一样,所以 CM3 需要手动把入口地址最低位清 0。
— freertos初始化栈帧的时候,为什么要 cm3 把函数入口地址的最低位清0(Jack)
二、任务栈
怎么估算任务栈的大小?
开发期先估:TCB(任务控制块)大小 + 预估栈深×4;再用 uxTaskGetStackHighWaterMark() 看该任务自启动以来的最小剩余栈,得到最大使用量,一般再乘 1.5 作为最终分配值。
— 怎么估算任务栈的大小?根据任务中创建变量的多少(Jack)
裸机下如何实测栈使用峰值?
重定位时把整块栈预填充为特定值("涂油漆"),运行一段时间后扫描特定值还剩在哪里,被覆盖到的部分就是峰值用量——最常用的测峰值方法。
— 大佬们,stm32裸机如何实时查看堆栈的使用情况,有时候分配失败又不知道要分配多少(Jack)
vTaskList 打印出来的任务栈一直是 0?
多半不是栈不够,而是传给 vTaskList() 的 task_info_buffer 太小、数据打印不全,把缓冲区调大即可。
— 老师,key_task的任务栈空间明明设置很大(Jack)
三、调度机制
抢占式调度和协作式调度是什么关系?
抢占式是 OS 层的调度配置:更高优先级任务可以在下一个 ostick 抢占低优先级任务。协作式更多是程序设计行为——比如每个任务都主动加延时、主动让出。两者不互斥。
— 抢占式调度和协作式调度 当你(Jack)
上下文切换多久发生一次?
configUSE_PREEMPTION==1(抢占)+ configUSE_TIME_SLICING==1(时间片轮转)时,轮转周期就是一个 SysTick 中断周期,即一个 RTOS 节拍,由 configTICK_RATE_HZ 决定(1000 = 每 1ms 一次 tick 中断),tick 中断里再触发 PendSV 检查是否需要切换或抢占。
— 老师,在FREERTOS进行上下文切换的时候(Jack)
高优先级任务就绪后,要等当前任务的时间片用完吗?
不用。信号量本质还是队列:释放信号量或写队列时,队列处理函数会立刻检查是否有更高优先级任务因此被唤醒,有则立即触发任务切换,不会等剩余时间片耗尽。
— 在FreeRTOS中,当任务1运行了0.5ms时释放信号量(Jack)
同题变体:— 老师,如果FreeRTOS是抢占式时间片调度模式下(Jack)(注意该帖答句首写"后者",但随后的解释"立刻发生任务切换"与前帖一致,按机制应为立即切换)
被挂起的任务,恢复后要继续睡完 osDelay 吗?
不用。挂起会把任务从 delay 造成的阻塞直接转到就绪:即使 osDelay(xx) 时间未到,resume 之后任务立即就绪。
— 挂起任务会直接从delay导致的阻塞直接转到就绪(Jack)
任务在 memcpy 拷贝一半时被中断/时间片切走,数据会坏吗?
会还原。任务切换靠硬件入栈保存上下文,切回来时现场原样恢复;memcpy 只是一个被暂停的拷贝过程,下次接着跑。
— 老师,RTOS中,线程在memcpy()的时候,如果被中断切走或者时间片用完(Jack)
四、优先级设计
消费者任务优先级为什么通常低于生产者?
为了优先保证数据源的持续供应,防止生产者因缓冲区满而阻塞,避免系统吞吐量下降或数据丢失。
— 老师,为什么消费者线程优先级小于生产者线程(Jack)
需要运行频率高于时基(比如 1kHz 以上、每秒 1000 次)的任务怎么做?
设一个更高频率的定时器,在定时器中断里释放信号量给该任务(设为最高优先级,平时始终阻塞);中断服务函数离开时置位 PendSV,退出中断后立刻切换到这个任务。另外可用看门狗线程保证其实时性。
— 老师:如果freertos需要创建一个频率高于1khz的任务(一般rtos时基就1m(Jack)
来源笔记
- xTaskCreate(LcdPrintTask, :task1:, 128,(Jack)
- 动态静态创建任务: 你动态创(Jack)
- 如果freertos中创建的任务没有死循环,会发生什么呢:(Jack)
- freertos初始化栈帧的时候,为什么要 cm3 把函数入口地址的最低位清0(Jack)
- 怎么估算任务栈的大小?根据任务中创建变量的多少(Jack)
- 大佬们,stm32裸机如何实时查看堆栈的使用情况,有时候分配失败又不知道要分配多少(Jack)
- 老师,key_task的任务栈空间明明设置很大(Jack)
- 抢占式调度和协作式调度 当你(Jack)
- 老师,在FREERTOS进行上下文切换的时候(Jack)
- 在FreeRTOS中,当任务1运行了0.5ms时释放信号量(Jack)
- 老师,如果FreeRTOS是抢占式时间片调度模式下(Jack)
- 挂起任务会直接从delay导致的阻塞直接转到就绪(Jack)
- 老师,RTOS中,线程在memcpy()的时候,如果被中断切走或者时间片用完(Jack)
- 老师,为什么消费者线程优先级小于生产者线程(Jack)
- 老师:如果freertos需要创建一个频率高于1khz的任务(一般rtos时基就1m(Jack)